#include <gtest/gtest.h>
#include <cstddef>

#include "common_components/common_runtime/src/common/page_cache.h"
#include "common_components/common_runtime/src/common/page_pool.h"
#include "common_components/common_runtime/src/common/mem_common.h"
#include "common_components/common_runtime/src/base/log_file.h"

using namespace panda;

namespace {

// ====== 手动定义私有字段偏移量 ======
// 这些偏移量需要在构建时一次性确认（见下方说明）
constexpr size_t OFFSET_pageCacheSpans_ = 8;   // 示例偏移，需根据实际结构确定
constexpr size_t OFFSET_idSpanMap_ = 152;     // 示例偏移，需根据实际结构确定

// ====== 工具函数：基于偏移访问私有字段 ======

template <typename T>
T& get_member(void* obj, size_t offset) {
    return *reinterpret_cast<T*>(reinterpret_cast<char*>(obj) + offset);
}

} // namespace

TEST(PageCacheTest, GetInstance_ReturnsSingleton) {
    PageCache* instance1 = PageCache::GetInstance();
    PageCache* instance2 = PageCache::GetInstance();

    EXPECT_EQ(instance1, instance2);
}

TEST(PageCacheTest, NewSpan_AllocatesFromBucketIfAvailable) {
    PageCache& cache = *PageCache::GetInstance();

    auto& pageCacheSpans = get_member<SpanList[128]>(std::addressof(cache), OFFSET_pageCacheSpans_);

    Span* testSpan = new Span();
    testSpan->pageNum = 4;
    testSpan->pageId = 100;

    pageCacheSpans[4].PushFront(testSpan);

    Span* result = cache.NewSpan(4);  // 应该从 bucket[4] 拿取
    EXPECT_EQ(result, testSpan);
    EXPECT_TRUE(pageCacheSpans[4].Empty());
}

TEST(PageCacheTest, MapObjectToSpan_ReturnsCorrectSpan) {
    PageCache& cache = *PageCache::GetInstance();

    auto& idSpanMap = get_member<std::unordered_map<pageID, Span*>>(std::addressof(cache), OFFSET_idSpanMap_);

    Span* span = new Span();
    span->pageNum = 4;
    span->pageId = 100;

    for (size_t i = 0; i < span->pageNum; ++i) {
        idSpanMap[span->pageId + i] = span;
    }

    void* obj = reinterpret_cast<void*>(static_cast<uintptr_t>(span->pageId << PAGE_SHIFT));
    Span* result = cache.MapObjectToSpan(obj);

    EXPECT_EQ(result, span);
}

TEST(PageCacheTest, ReleaseSpanToPageCache_MergesWithPrevious) {
    PageCache& cache = *PageCache::GetInstance();

    auto& pageCacheSpans = get_member<SpanList[128]>(std::addressof(cache), OFFSET_pageCacheSpans_);
    auto& idSpanMap = get_member<std::unordered_map<pageID, Span*>>(std::addressof(cache), OFFSET_idSpanMap_);

    Span* prev = new Span();
    prev->pageNum = 2;
    prev->pageId = 100;
    prev->isUse = false;
    pageCacheSpans[2].PushFront(prev);

    Span* target = new Span();
    target->pageNum = 3;
    target->pageId = 102;
    target->isUse = true;

    for (size_t i = 0; i < prev->pageNum; ++i) {
        idSpanMap[prev->pageId + i] = prev;
    }
    for (size_t i = 0; i < target->pageNum; ++i) {
        idSpanMap[target->pageId + i] = target;
    }

    cache.ReleaseSpanToPageCache(target);

    EXPECT_EQ(target->pageNum, 5U);
    EXPECT_EQ(target->pageId, 100U);
    EXPECT_FALSE(target->isUse);
}